package varnam_manip

import (
	"fmt"
	"go/ast"
	"manip_exec"
)

type VarManipulator struct {
}

var astNodes []ast.Node
var Idents []*ast.Ident

func (m VarManipulator) ManipulateAST(f ast.Node, index int) (manip_exec.ManiRes, error) {

	astNodes = make([]ast.Node, 0)		//This has to be reset as well
	Idents = make([]*ast.Ident, 0)

	// Walk through the AST & Build astNodes
	v := astVisitor{}
	ast.Walk(v, f)

	Funcs := make([]*ast.FuncDecl, 0)
	//Idents := make([]*ast.Ident, 0)

	for _, v := range astNodes {

		switch x := v.(type) {
		case *ast.FuncDecl:
				Funcs = append(Funcs, x) //Take the rest
		}
	}

	var random int
	if len(Funcs) > 0 {
		random = index % len(Funcs)
		//fmt.Println("In ", Funcs[random].Name)
	} else {		//Force build failure
	retString := "Nothing manipulated"
	result := manip_exec.ManiRes{[]int{}, retString}
	return result, nil		
	}

	fv := funcVisitor{}
	ast.Walk(fv, Funcs[random])
	//fmt.Println(len(Idents), Idents)

	if len(Idents) > 0 {
		random = index % len(Idents)
		//fmt.Println(len(Idents), Idents, random)
	} else {		//Force build failure
	retString := "Nothing manipulated"
	result := manip_exec.ManiRes{[]int{}, retString}
	return result, nil		
	}

	changed, poss := m.swap(random, len(Idents)-1-random, Idents)
	result := manip_exec.ManiRes{poss, changed}
	return result, nil


	// var random int
	// if len(Idents) > 0 {
	// random = index % len(Idents)
	// } else {		//Force build failure
	// 	retString := "Nothing manipulated"
	// 	result := manip_exec.ManiRes{[]int{}, retString}
	// 	return result, nil		
	// }

	// changed, poss := m.swap(random, len(Idents)-1-random, Idents)
	// result := manip_exec.ManiRes{poss, changed}
	// return result, nil

}

func (m VarManipulator) swap(x, y int, Idents []*ast.Ident) (string, []int) {

	if (Idents[x].String() != Idents[y].String()) {
	temp := Idents[x].Name
	Idents[x].Name = Idents[y].Name
	Idents[y].Name = temp

	retString := fmt.Sprintf("Swapped %s with %s in Lines: ", Idents[y], Idents[x])
	poss := make([]int, 0)
	poss = append(poss, int(Idents[x].Pos()))
	poss = append(poss, int(Idents[y].Pos()))

	return retString, poss
	}

	retString := "Nothing manipulated"
	return retString, make([]int, 0)
}

func (m VarManipulator) String() string {
	return "varnam"
}

//Walker Vistor struct
type astVisitor struct {
}

type funcVisitor struct {
}

//Walker Visitor function
func (v astVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node != nil {

		astNodes = append(astNodes, node) //Builds the Node Slice
	}
	return ast.Visitor(v)
}

func (v funcVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node != nil {
		switch x := node.(type) {
		case *ast.Ident:
			switch x.String() {
				case "int", "panic":	//Ignore go keywords
				default:
				Idents = append(Idents, x)
			}
		}
	}
	return ast.Visitor(v)
}
